#include "ClipPlayer.h"

ClipPlayer::ClipPlayer(void)
{
}

ClipPlayer::~ClipPlayer(void)
{
}

bool ClipPlayer::initialise(HINSTANCE hInst)
{
	mfNewClipCallback = NULL;
	mfClipStackEmptyCallback = NULL;
	//mfWindowClosedCallback = NULL;
	mpCurrentClip = NULL;
	mpMediaControl = NULL;
	mpMediaEvent = NULL;
	mpGraphBuilder = NULL;
	mpMediaSeeking = NULL;

	if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
	{
		return false;
	}

	if(!hasVMR9())
	{
		return false;
	}

	WNDCLASS windowClass;
	ZeroMemory(&windowClass,sizeof(windowClass));

	windowClass.hInstance = hInst;
	windowClass.lpfnWndProc = MsgProc;
	windowClass.lpszClassName = "QRHERE";
	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowClass.hIcon = NULL;

	if(!RegisterClass(&windowClass))
	{
		return false;
	}

	mHwnd = CreateWindow("QRHERE","QRHere", WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0, NULL, 0);

	if(FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&mpGraphBuilder)))
	{
		return false;
	}

	SmartPtr<IBaseFilter> pVidMixingRenderers = NULL;
	if(FAILED(CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVidMixingRenderers)))
	{
		return false;
	}

	if(FAILED(mpGraphBuilder->AddFilter(pVidMixingRenderers, L"QRHERE")))
	{
		return false;
	}

	SmartPtr<IVMRFilterConfig9> pConfig;
	pVidMixingRenderers->QueryInterface(IID_IVMRFilterConfig9, (void**)&pConfig);
	pConfig->SetRenderingMode(VMR9Mode_Windowless);

	if(FAILED(pVidMixingRenderers->QueryInterface(IID_IVMRWindowlessControl9, (void**)&mpWindowlessControl)))
	{
		return false;
	}

	mpWindowlessControl->SetVideoClippingWindow(mHwnd);
	//mpWindowlessControl->SetBorderColor(RGB(0,0,0));

	return true;
}

bool ClipPlayer::beginPlayback(const std::string& videoFile)
{
	size_t needed = ::mbstowcs(NULL,&videoFile[0],videoFile.length());

	std::wstring output;
	output.resize(needed);

	::mbstowcs(&output[0],&videoFile[0],videoFile.length());

	const WCHAR* p = output.c_str();
	
	if(FAILED(RenderFileToVideoRenderer(mpGraphBuilder,const_cast<WCHAR*>(p), true)))
	{
		return false;
	}

	mpGraphBuilder->QueryInterface(IID_IMediaControl, (void**)&mpMediaControl);
	mpGraphBuilder->QueryInterface(IID_IMediaEventEx, (void**)&mpMediaEvent);
	mpGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&mpMediaSeeking);
    //mpGraphBuilder->QueryInterface(IID_IMediaPosition, (void **)&mpMediaPosition);
    //mpGraphBuilder->QueryInterface(IID_IBasicAudio, (void **)&mpBasicAudio);
	
	//mpMediaEvent->SetNotifyWindow((OAHWND)mHwnd, WM_GRAPHNOTIFY, 0);

	long width;
	long height;

	if(FAILED(mpWindowlessControl->GetNativeVideoSize(&width, &height, NULL, NULL)))
	{
		return false;
	}

	int titleHeight = GetSystemMetrics(SM_CYCAPTION);
	int borderWidth = GetSystemMetrics(SM_CXBORDER);
	int borderHeight = GetSystemMetrics(SM_CYBORDER);

	SetWindowPos(mHwnd, NULL, 0, 0, width+(2*borderWidth), height+titleHeight+(2*borderHeight), SWP_NOMOVE | SWP_NOOWNERZORDER );
	GetClientRect(mHwnd, &mRenderTarget);

	if(FAILED(mpWindowlessControl->SetVideoPosition(NULL, &mRenderTarget)))
	{
		return false;
	}

	//ShowWindow(mHwnd, SW_SHOWNORMAL);
	//UpdateWindow(mHwnd);
	//SetForegroundWindow(mHwnd);

	mpMediaControl->Run();

	//SetFocus(mHwnd);

	playNextClip();
}

void ClipPlayer::destroy(void)
{
	if(mpMediaControl)
	{
		mpMediaControl->Stop();
		mpMediaControl->Release();
	}

	if(mpMediaEvent)
	{
		mpMediaEvent->SetNotifyWindow((OAHWND)NULL,0,0);
		mpMediaEvent->Release();
	}

	if(mpMediaSeeking)
		mpMediaSeeking->Release();

	if(mpGraphBuilder)
		mpGraphBuilder->Release();
}

bool ClipPlayer::hasVMR9(void)
{
	IBaseFilter* baseFilter = NULL;

	if(FAILED(CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID*)&baseFilter)))
	{
		return false;
	}

	baseFilter->Release();

	return true;
}

double ClipPlayer::getPosition(void)
{
	LONGLONG pos;
	if(FAILED(mpMediaSeeking->GetCurrentPosition(&pos)))
		return -1;
	
	return (double)pos/10000000;
}

void ClipPlayer::update(void)
{
	if(getPosition() >= mpCurrentClip->mEnd)
	{
		playNextClip();
	}
}

void ClipPlayer::pushClip(Clip* pClip, bool clear)
{
	if(clear)
	{
		while(!mClips.empty())
		{
			delete mClips.top();
			mClips.pop();
		}
	}

	mClips.push(pClip);
}

void ClipPlayer::playNextClip(void)
{
	CLIP_TYPE type = TYPE_UNKNOWN;
	if(mpCurrentClip)
		type = mpCurrentClip->mType;

	if(mClips.empty())
	{
		mfClipStackEmptyCallback();
	}
	
	mpCurrentClip = mClips.top();
	mClips.pop();

	if(mfNewClipCallback)
		mfNewClipCallback(type, mpCurrentClip->mType);
	seekToPosition(mpCurrentClip->mBegin);
}

void ClipPlayer::seekToPosition(REFTIME time)
{
	LONGLONG llTime = (LONGLONG)time * double(10000000);

    if(mpMediaSeeking != NULL)
    {
        FILTER_STATE fs;
        mpMediaControl->GetState(100, (OAFilterState *)&fs);
        mpMediaSeeking->SetPositions(&llTime, AM_SEEKING_AbsolutePositioning, NULL, 0);
	}
}

void ClipPlayer::setNewClipCallback(void(*pCallback)(CLIP_TYPE,CLIP_TYPE))
{
	mfNewClipCallback = pCallback;
}

void ClipPlayer::setClipStackEmptyCallback(void(*pCallback)(void))
{
	mfClipStackEmptyCallback = pCallback;
}

LRESULT CALLBACK ClipPlayer::MsgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if(message == WM_QUIT || message == WM_CLOSE)
	{
		PostQuitMessage(0);
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}